/**
 * @author jiazheng.xu
 * @description 海报图合成方案。
 * 使用说明：
 * 1. new一个实例会在body中插入两个辅助div，zIndex层级在div以下，body中内容需要设置背景色非透明。
 * 2. 实例在不需要时，手动执行distory方法，移除相应的dom和事件绑定。
 * 3. 内部将img的一些修改做了处理，外部无需再做处理。
 * 4. 创建多个实例时，只会插入一个预览dom，所以不能同时多个实例使用previewDom.show()
 */

import html2canvas from 'html2canvas';

// 按需加载
let Xtemplate = null;

// 设置dom样式
const setDomStyle = (dom, styleObj) => {
    Object.keys(styleObj).forEach((attr) => {
        dom.style[attr] = styleObj[attr];
    });
};

// 文本溢出截取函数
export const textOverflow = ({
    width, // 容器宽度
    line, // 行数
    lineHeight, // 行高
    fontSize, // 字体大小
    text, // 文本内容
}) => {
    // 添加辅助dom到页面
    const pDom = document.createElement('p');

    setDomStyle(pDom, {
        width: `${width}px`,
        fontSize: `${fontSize}px`,
        lineHeight: `${lineHeight}px`,
        position: 'fixed',
        top: '-100%',
    });

    pDom.innerHTML = text;
    document.body.appendChild(pDom);

    let height = line * lineHeight;

    if (pDom.offsetHeight <= height) {
        return text;
    }
    // 计算文本
    let ext = '...';
    let handleText = text;
    let resultText;

    const changeDomText = () => {
        resultText = handleText + ext;
        pDom.innerHTML = resultText;
    };
    changeDomText();

    while (pDom.offsetHeight > height) {
        handleText = handleText.slice(0, handleText.length - 1);
        changeDomText();
    }
    document.body.removeChild(pDom);
    return resultText;
};

class MemHtmlCanvas {
    // 获取默认参数
    static getDefaiultConfig() {
        return {
            preview: true, // 预览功能
            onlyCanvas: false, // 只合成canvas
            scale: 2, // 默认2倍
        };
    }
    // 获取8位随机字符id
    static getUniqueId() {
        return Math.random().toString(36)
            .slice(-4) + Date.now().toString(36)
            .slice(-4);
    }
    // 设置样式
    static setStyle(divDom, styleObj) {
        Object.keys(styleObj).forEach((attr) => {
            divDom.style[attr] = styleObj[attr];
        });
    }
    // 创建dom并追加到body
    static createDom({className, style: styleObj}) {
        const {setStyle} = MemHtmlCanvas;
        const divDom = document.createElement('div');
        divDom.className = className;
        setStyle(divDom, styleObj);
        divDom.hide = function () {
            setStyle(this, {
                zIndex: -20,
                visibility: 'hidden',
            });
        };
        divDom.show = function () {
            setStyle(this, {
                zIndex: 99,
                visibility: 'visible',
            });
        };
        document.body.appendChild(divDom);
        return divDom;
    }
    // 样式集合
    static _getCustomDomStyle(type = 'preview') {
        const styleCache = {
            // 预览带遮罩dom样式
            preview: {
                position: 'fixed',
                zIndex: -20,
                left: 0,
                top: 0,
                width: '100%',
                height: '100%',
                background: 'rgba(0, 0, 0, 0.6)',
                display: 'flex',
                visibility: 'hidden',
                justifyContent: 'center',
                alignItems: 'center',
            },
            // 渲染数据dom样式
            render: {
                position: 'absolute',
                zIndex: -20,
                top: '-400%',
                left: 0,
            },
        };
        return styleCache[type] || styleCache.preview;
    }
    // 获取预览dom
    static _getPreviewDom(bbObj) {
        if (!MemHtmlCanvas._preview_dom_) {
            MemHtmlCanvas._preview_dom_ = MemHtmlCanvas.createDom({
                className: 'mem-html-canvas-wrap',
                style: MemHtmlCanvas._getCustomDomStyle('preview'),
            });
            MemHtmlCanvas._preview_dom_.addEventListener('click', MemHtmlCanvas._listenHandle.bind(bbObj), false);
        }
        return MemHtmlCanvas._preview_dom_;
    }
    // 获取渲染dom
    static _getRenderDom(vStr) {
        if (!MemHtmlCanvas[`_render_dom_${vStr}`]) {
            MemHtmlCanvas[`_render_dom_${vStr}`] = MemHtmlCanvas.createDom({
                className: `mem-html-canvas-render-${vStr}`,
                style: MemHtmlCanvas._getCustomDomStyle('render'),
            });
            const renderDomList = (MemHtmlCanvas.renderDomList || []);
            renderDomList.push(vStr);
            MemHtmlCanvas.renderDomList = renderDomList;
        }
        return MemHtmlCanvas[`_render_dom_${vStr}`];
    }
    // 预览dom点击监听事件
    static _listenHandle(e) {
        if (e.target === e.currentTarget) {
            this.previewDom.hide();
        }
    }
    // 渲染dom片段
    static renderFragment(dom, config = {}) {
        const showDom = config.showDom;
        const bbObj = new MemHtmlCanvas('internal', config);
        const cloneDom = dom.cloneNode(true);
        MemHtmlCanvas.setStyle(bbObj.renderDom, {
            width: `${dom.offsetWidth}px`,
            height: `${dom.offsetHeight}px`,
        });
        bbObj.renderDom.appendChild(cloneDom);

        MemHtmlCanvas.handleImageForDom(bbObj.renderDom, bbObj);
        return showDom ? bbObj.renderView() : bbObj.getHtml2CanvasObject(bbObj.renderDom);
    }
    // 判断是否是base64链接
    static isBase64Image(src) {
        return /^data:image\/.*;base64,/i.test(src);
    }
    // 补全img链接
    static fullImageSrc(src) {
        return /http(s)?:/.test(src) ? src : window.location.protocol + src;
    }
    // 添加自定义参数
    static appendPrivateParam(src, v) {
        return `${src}${~src.indexOf('?') ? '&' : '?'}mem_html_canvas_v1`;
    }
    // 处理dom图片
    static handleImageForDom(dom, bbObj) {
        const {
            isBase64Image,
            fullImageSrc,
            appendPrivateParam,
        } = MemHtmlCanvas;

        Array.prototype.slice.call(dom.querySelectorAll('img')).forEach((imgDom) => {
            if (imgDom.getAttribute('crossorigin') !== 'anonymous' && !bbObj.config.onlyCanvas) {
                imgDom.setAttribute('crossorigin', 'anonymous');
            }
            let src = imgDom.src;
            if (!isBase64Image(src)) {
                src = appendPrivateParam(fullImageSrc(src), bbObj._v);
                imgDom.src = src;
            }
        });
    }
    // 渲染dom的布局内容
    static setRenderContent(bbObj, data) {
        const {template, renderDom} = bbObj;
        let html = template.render(data);
        renderDom.innerHTML = html;

        MemHtmlCanvas.setStyle(renderDom, {
            width: `${renderDom.offsetWidth}px`,
            height: `${renderDom.offsetHeight}px`,
        });

        MemHtmlCanvas.handleImageForDom(renderDom, bbObj);
    }
    static createPreviewImg(imgUrl) {
        return `<img style="width: 70%;" src="${imgUrl}" alt="生成海报图" ／>`;
    }
    // 由于ios上canvas画图面积的大小限制， 计算ios canvas尺寸和缩放比, 目前由于ios8不支持一些属性，暂无使用
    // static computedIOSCanvasSize(renderDom) {
    //     const maxSizeL9 = 2096;
    //     const maxSizeG9 = 4096;

    //     const domWidth = renderDom.offsetWidth;
    //     const domHeight = renderDom.offsetHeight;

    //     const dpr = window.devicePixelRatio;

    //     let maxSize = currentVersion > 9 ? maxSizeG9 : maxSizeL9;
    //     let canvasWidth;
    //     let canvasHeight;
    //     let scale = 1;

    //     if (domWidth * dpr <= maxSize && domHeight * dpr <= maxSize) {
    //         canvasWidth = domWidth;
    //         canvasHeight = domHeight;
    //     } else {
    //         let widthCutNum = (domWidth * dpr) - maxSize;
    //         let heightCutNum = (domHeight * dpr) - maxSize;
    //         // 宽度超出大，以宽度缩小
    //         if (widthCutNum > heightCutNum) {
    //             canvasWidth = maxSize / dpr;
    //             scale = canvasWidth / domWidth;
    //             canvasHeight = (maxSize / domWidth / dpr) * domHeight;
    //         } else {
    //             canvasHeight = maxSize / dpr;
    //             scale = canvasHeight / domHeight;
    //             canvasWidth = (maxSize / domHeight / dpr) * domWidth;
    //         }
    //     }

    //     return {
    //         width: canvasWidth,
    //         height: canvasHeight,
    //         scale,
    //     };
    // }
    constructor(template, config) {
        if (!template) {
            throw new Error('传递模板无效');
        }
        if (!Xtemplate) {
            Xtemplate = require('xtemplate/lib/runtime');
        }
        const {
            _getPreviewDom,
            _getRenderDom,
            getUniqueId,
            getDefaiultConfig,
        } = MemHtmlCanvas;

        if (template && template !== 'internal') {
            this.template = new Xtemplate(template);
        }
        this._v = getUniqueId();
        this.config = Object.assign({}, getDefaiultConfig(), (config && typeof config === 'object' && config));

        this.renderDom = _getRenderDom(this._v);
        if (this.config.preview) {
            this.previewDom = _getPreviewDom(this);
        }
    }
    // 渲染布局dom
    renderView() {
        const {previewDom, renderDom} = this;
        const renderDomClone = renderDom.cloneNode(true);
        MemHtmlCanvas.setStyle(renderDomClone, {
            top: 0,
        });
        previewDom.appendChild(renderDomClone);
        return Promise.resolve({
            previewDom,
        });
    }
    // 渲染canvas合成图
    renderCanvas() {
        const {renderDom} = this;

        return this.getHtml2CanvasObject(renderDom);
    }
    render(data, showDom = false) {
        MemHtmlCanvas.setRenderContent(this, data);
        return showDom ? this.renderView(data) : this.renderCanvas(data);
    }
    // 获取html2canvas返回的promise对象
    getHtml2CanvasObject(renderDom) {
        const {config, previewDom} = this;
        const {
            onlyCanvas,
            preview,
            scale,
        } = config;
        let html2CanvasOptions = {
            useCORS: !onlyCanvas,
            allowTaint: onlyCanvas,
            logging: true,
            scale, // 放大比例默认为2， 为了兼容ios问题
        };
        const html2canvasObject = html2canvas(renderDom, html2CanvasOptions);

        return preview ? html2canvasObject.then((canvas) => {
            let resultObj = {
                canvas,
            };
            if (preview) {
                resultObj.previewDom = previewDom;
                if (onlyCanvas) {
                    previewDom.appendChild(canvas);
                } else {
                    const imgUrl = canvas.toDataURL();
                    previewDom.innerHTML = MemHtmlCanvas.createPreviewImg(imgUrl);
                    resultObj.imgUrl = imgUrl;
                }
            }

            return resultObj;
        }) : html2canvasObject.then((canvas) => ({
            canvas,
        }));
    }
    // 注销实例
    distory() {
        // 删除dom和缓存
        let renderDomList = MemHtmlCanvas.renderDomList;
        document.body.removeChild(this.renderDom);
        renderDomList = renderDomList.filter((item) => item !== this._v);
        if (!renderDomList.length) {
            const previewDom = MemHtmlCanvas._preview_dom_;
            previewDom.removeEventListener('click', MemHtmlCanvas._listenHandle);
            document.body.removeChild(previewDom);
        }
    }
}

export default MemHtmlCanvas;
